package com.google.com.coma.tester.space;

import com.google.code.coma.Coma;
import com.google.code.coma.domain.Computer;
import com.google.code.coma.domain.LifeCycleStep;
import com.google.code.coma.domain.Part;
import com.google.code.coma.space.ComputerSelectorCallback;
import com.google.code.coma.space.SpaceSelector;
import com.google.code.coma.space.util.SpaceManager;
import com.google.code.coma.worker.Worker;
import com.google.com.coma.tester.AbstractTesterWorker;
import com.google.com.coma.tester.Tester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozartspaces.core.*;

import java.net.URI;
import java.util.List;

/**
 * @author Bernhard Nickel, e0925384@student.tuwien.ac.at
 */
public class SpaceTesterWorker extends AbstractTesterWorker implements Worker {
    private static final Log log = LogFactory.getLog(SpaceTesterWorker.class);

	private MzsCore core;

	private Capi capi;

	private ContainerReference computers;
    private ContainerReference parts;

	private Thread executionThread;

	private Tester tester;

	private URI spaceUri;

	public SpaceTesterWorker(String factoryId, Tester tester, LifeCycleStep sourceStep) {
		super(factoryId, sourceStep);

		this.tester = tester;

		core = DefaultMzsCore.newInstance(0);
		capi = new Capi(core);
		spaceUri = URI.create("xvsm://localhost:4242");
		try {
			computers = SpaceManager.getOrCreateNamedContainer(spaceUri, factoryId + "_" + Coma.COMPUTER_CONTAINER, capi);
            parts = SpaceManager.getOrCreateNamedContainer(spaceUri, factoryId + "_" + Coma.PARTS_CONTAINER, capi);
		} catch (MzsCoreException e) {
			core.shutdown(false);
			throw new RuntimeException("Unable to get container", e);
		}
	}


	@Override
	public void startWork() {
		executionThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (!Thread.currentThread().isInterrupted()) {
					TransactionReference tx;
					try {
						tx = capi.createTransaction(MzsConstants.TransactionTimeout.INFINITE, spaceUri);
					} catch (MzsCoreException e) {
						log.error("Unable to create transaction", e);
						return;
					}
					try {
                        List<Computer> takenComputers = capi.take(computers, new SpaceSelector(new ComputerSelectorCallback(sourceStep)), MzsConstants.RequestTimeout.INFINITE, tx);
						Computer computer = takenComputers.get(0);

                        tester.testComputer(computer, getWorkerId());

                        if (computer.getCurrent().equals(LifeCycleStep.REJECTED)) {
                            for (Part part : getNoneDefectComputerParts(computer)) {
                                capi.write(new Entry(part), parts, MzsConstants.RequestTimeout.INFINITE, tx);
                            }
                        } else {
                            capi.write(new Entry(computer), computers, MzsConstants.RequestTimeout.INFINITE, tx);
                        }

                        capi.commitTransaction(tx);
					} catch (MzsCoreException e) {
						log.error("Space exception", e);
						try {
							capi.rollbackTransaction(tx);
						} catch (MzsCoreException e1) {
							log.error("Error in transaction rollback", e);
						}
						Thread.currentThread().interrupt();
					}
				}
			}
		});
		executionThread.start();
	}

	@Override
	public void stopWork() {
		if (executionThread != null) {
			executionThread.interrupt();
		}
	}

    @Override
    public boolean isActive() {
        return executionThread.isAlive();
    }
}
